feat(security): Add audience and issuer validation to OIDC JWT authentication#26781
feat(security): Add audience and issuer validation to OIDC JWT authentication#26781kimsehwan96 wants to merge 1 commit intoopen-metadata:mainfrom
Conversation
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Outdated
Show resolved
Hide resolved
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Show resolved
Hide resolved
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Adds issuer (iss) and audience (aud) validation to OpenMetadata’s JWT authentication flow to prevent cross-client token reuse when using OIDC, while keeping internal OpenMetadata-issued tokens working.
Changes:
- Add
iss/audchecks inJwtFilter.validateJwtAndGetClaims()whenauthority/clientIdare configured, with a bypass path for internal tokens. - Validate
iss/audfor the OIDC authorization code flow ID token handling. - Expand
JwtFilterTestcoverage for issuer/audience validation scenarios; exposeJWTTokenGenerator.issuervia Lombok@Getter.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java | Adds configured iss/aud validation and internal-token bypass logic during JWT claim extraction. |
| openmetadata-service/src/main/java/org/openmetadata/service/security/AuthenticationCodeFlowHandler.java | Adds iss/aud validation for the ID token received in the OIDC code flow. |
| openmetadata-service/src/main/java/org/openmetadata/service/security/jwt/JWTTokenGenerator.java | Exposes the internal JWT issuer via getter for internal token identification. |
| openmetadata-service/src/test/java/org/openmetadata/service/security/JwtFilterTest.java | Adds unit tests covering audience/issuer validation and bypass behavior. |
...a-service/src/main/java/org/openmetadata/service/security/AuthenticationCodeFlowHandler.java
Outdated
Show resolved
Hide resolved
| boolean isInternalToken = | ||
| internalJwtIssuer != null && internalJwtIssuer.equals(jwt.getIssuer()); |
There was a problem hiding this comment.
isInternalToken is determined solely by comparing the token iss claim to the configured internal JWT issuer. If an operator configures the internal jwtissuer to match the external OIDC issuer (or they coincide), then external OIDC tokens would be treated as “internal” and bypass iss/aud validation, reintroducing the cross-client token reuse risk this PR is addressing. Consider identifying internal tokens using a stronger signal tied to the signing key (e.g., token kid matching the locally configured JWKS key id) and/or the presence of OpenMetadata-only claims (like tokenType), rather than issuer string equality alone.
| boolean isInternalToken = | |
| internalJwtIssuer != null && internalJwtIssuer.equals(jwt.getIssuer()); | |
| Claim tokenTypeClaim = jwt.getClaim(TOKEN_TYPE); | |
| boolean hasInternalTokenTypeClaim = tokenTypeClaim != null && !tokenTypeClaim.isNull(); | |
| boolean isInternalToken = | |
| internalJwtIssuer != null | |
| && internalJwtIssuer.equals(jwt.getIssuer()) | |
| && hasInternalTokenTypeClaim; |
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Show resolved
Hide resolved
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Outdated
Show resolved
Hide resolved
|
Thanks @kimsehwan96 for the PR. please check the co-pilot reivew comments if they are relevant to address |
|
The Java checkstyle failed. Please run You can install the pre-commit hooks with |
8e427a0 to
2edf2f4
Compare
|
I added more changes.
I've tested this with Authentik (custom-oidc). If anyone can verify with Azure AD or similar, that would be appreciated. |
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Outdated
Show resolved
Hide resolved
🟡 Playwright Results — all passed (18 flaky)✅ 3413 passed · ❌ 0 failed · 🟡 18 flaky · ⏭️ 209 skipped
🟡 18 flaky test(s) (passed on retry)
How to debug locally# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip # view trace |
2edf2f4 to
3e88eea
Compare
|
The Java checkstyle failed. Please run You can install the pre-commit hooks with |
3e88eea to
d894ac0
Compare
| String discoveryUrl = authority; | ||
| if (!discoveryUrl.endsWith("/")) { | ||
| discoveryUrl += "/"; | ||
| } | ||
| discoveryUrl += ".well-known/openid-configuration"; |
There was a problem hiding this comment.
resolveOidcIssuer performs an HTTP call to the IdP discovery endpoint during JwtFilter construction. If the IdP is slow/unreachable, this can delay startup (15s default timeout) and adds an external dependency to initialize the auth filter. Consider resolving/caching the issuer during configuration validation/save, or making discovery resolution explicitly optional/non-blocking with a shorter timeout.
...a-service/src/main/java/org/openmetadata/service/security/AuthenticationCodeFlowHandler.java
Show resolved
Hide resolved
openmetadata-service/src/test/java/org/openmetadata/service/security/JwtFilterTest.java
Show resolved
Hide resolved
openmetadata-service/src/test/java/org/openmetadata/service/security/JwtFilterTest.java
Outdated
Show resolved
Hide resolved
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Outdated
Show resolved
Hide resolved
openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java
Outdated
Show resolved
Hide resolved
…tication OpenMetadata's JWT token validation only checked signature and expiry, allowing cross-client token reuse within the same OIDC provider instance. Changes: - Add iss/aud claim validation in JwtFilter.validateJwtAndGetClaims() - Add iss/aud validation in AuthenticationCodeFlowHandler for OIDC code flow - Resolve actual OIDC issuer from discovery document instead of using authority URL directly (fixes Azure AD v2.0 and similar providers) - Normalize issuer comparison (trailing slash, case insensitive) - Internal tokens (bots, PATs) identified by matching internal JWT issuer bypass OIDC-specific validation - Error messages return generic info to clients, details logged server-side - Null guards for provider metadata and audience claims - Validation skipped when clientId/authority not configured (backward compat) - Expose JWTTokenGenerator.issuer via @Getter for internal token detection
d894ac0 to
29a5ee8
Compare
Code Review ✅ Approved 5 resolved / 5 findingsAdds audience and issuer validation to OIDC JWT authentication, resolving security issues including proper issuer validation using authority URLs, signature origin verification for internal tokens, sensitive error message redaction, locale-independent URL normalization, and RFC 3986-compliant path handling. ✅ 5 resolved✅ Bug: Issuer validation uses
|
| Auto-apply | Compact |
|
|
Was this helpful? React with 👍 / 👎 | Gitar
|



Describe your changes:
JWT token validation in OpenMetadata only checks signature and expiry, but does not validate
aud(audience) oriss(issuer) claims. This means a token issued by the same OIDC provider (e.g., Authentik) but for a different client ID can pass authentication — essentially a cross-client token reuse vulnerability.What I changed:
JwtFilter.validateJwtAndGetClaims()AuthenticationCodeFlowHandlerfor the OIDC code flowclientId/authorityis not configured for backward compatibility@GettertoJWTTokenGenerator.issuersoJwtFiltercan identify internal tokensType of change:
Checklist:
Fixes <issue-number>: <short explanation>